package com.jijunpeng.javafx.cipher.controller;

import com.jijunpeng.javafx.cipher.common.ComboTxtConstants;
import com.jijunpeng.javafx.cipher.common.DialogUtil;
import com.jijunpeng.javafx.cipher.controller.api.BaseController;
import com.jijunpeng.javafx.cipher.exception.InputTypeException;
import com.jijunpeng.javafx.cipher.exception.OutputTypeException;
import com.jijunpeng.javafx.cipher.persistence.PersistenceJson;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Pane;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

import java.io.UnsupportedEncodingException;

/**
 * @author Ji Junpeng
 * @date 2019-11-30 18-46
 */
@Slf4j
@PersistenceJson(path = "$.main_inner_$base64")
public class Base64Controller extends BaseController {
    @FXML
    private Pane base64RootPane;
    @FXML
    @PersistenceJson(path = "leftHexRb", field = "selected", getter = "isSelected")
    private RadioButton leftHexRb;
    @FXML
    @PersistenceJson(path = "leftStringRb", field = "selected", getter = "isSelected")
    private RadioButton leftStringRb;
    @FXML
    @PersistenceJson(path = "leftStringCharsetComboBox", field = "value")
    private ComboBox<String> leftStringCharsetComboBox;
    @FXML
    @PersistenceJson(path = "leftTextArea", field = "text")
    private TextArea leftTextArea;
    @FXML
    @PersistenceJson(path = "rightTextArea", field = "text")
    private TextArea rightTextArea;
    @FXML
    @PersistenceJson(path = "rightHexRb", field = "selected", getter = "isSelected")
    private RadioButton rightHexRb;
    @FXML
    @PersistenceJson(path = "rightBase64Rb", field = "selected", getter = "isSelected")
    private RadioButton rightBase64Rb;

    @Override
    protected Pane getRootPane() {
        return base64RootPane;
    }

    @Override
    protected void initViewData() {
        super.initViewData();
        ObservableList<String> charsetList = FXCollections.observableArrayList(ComboTxtConstants.LIST_CHARSET);
        leftStringCharsetComboBox.setItems(charsetList);
        leftStringCharsetComboBox.setValue(charsetList.get(0));
        leftStringRb.selectedProperty().addListener((observable, oldValue, newValue) -> leftStringCharsetComboBox.setDisable(!newValue));
    }

    /**
     * left --encode--> right
     */
    @FXML
    private void encode(ActionEvent actionEvent) {
        try {
            byte[] leftBytes = getLeftBytes();
            String outputStr = buildRightStr(leftBytes);
            rightTextArea.setText(outputStr);
            log.info("Base64 encode. inputType:{}, outputType:{}, inputStr:{}, outputStr:{}", getLeftType(), getRightType(), leftTextArea.getText(), outputStr);
        } catch (Exception e) {
            log.error("Base64 encode throw exception. inputType:{}, outputType:{}, inputStr:{}, e:", getLeftType(), getRightType(), leftTextArea.getText(), e);
            DialogUtil.showError(base64RootPane, "Base64 编码失败", "错误原因：" + e.getMessage());
        }
    }

    @FXML
    private void decode(ActionEvent actionEvent) {
        try {
            byte[] leftBytes = getRightBytes();
            String outputStr = buildLeftStr(leftBytes);
            leftTextArea.setText(outputStr);
            log.info("Base64 decode. inputType:{}, outputType:{}, inputStr:{}, outputStr:{}", getRightType(), getLeftType(), rightTextArea.getText(), outputStr);
        } catch (Exception e) {
            log.error("Base64 decode throw exception. inputType:{}, outputType:{}, inputStr:{}, e:", getRightType(), getLeftType(), rightTextArea.getText(), e);
            DialogUtil.showError(base64RootPane, "Base64 编码失败", "错误原因：" + e.getMessage());
        }
    }

    private String getLeftType() {
        if (leftHexRb.isSelected()) {
            return "Hex";
        }
        if (leftStringRb.isSelected()) {
            return "String";
        }
        return "";
    }

    private String getRightType() {
        if (rightHexRb.isSelected()) {
            return "Hex";
        }
        if (rightBase64Rb.isSelected()) {
            return "Base64";
        }
        return "";
    }

    private byte[] getLeftBytes() throws InputTypeException, UnsupportedEncodingException, DecoderException {
        String inputStr = leftTextArea.getText();
        byte[] resultBytes;
        if (leftHexRb.isSelected()) {
            resultBytes = Hex.decodeHex(inputStr);
        } else if (leftStringRb.isSelected()) {
            String charset = leftStringCharsetComboBox.getValue();
            resultBytes = inputStr.getBytes(charset);
        } else {
            throw new InputTypeException();
        }
        return resultBytes;
    }

    private byte[] getRightBytes() throws DecoderException, InputTypeException {
        String inputStr = rightTextArea.getText();
        byte[] resultBytes;
        if (rightHexRb.isSelected()) {
            resultBytes = Hex.decodeHex(inputStr);
            resultBytes = Base64.decodeBase64(resultBytes);
        } else if (rightBase64Rb.isSelected()) {
            resultBytes = Base64.decodeBase64(inputStr);
        } else {
            throw new InputTypeException();
        }
        return resultBytes;
    }

    private String buildLeftStr(byte[] outputBytes) throws UnsupportedEncodingException, OutputTypeException {
        String resultStr;
        if (leftHexRb.isSelected()) {
            resultStr = Hex.encodeHexString(outputBytes);
        } else if (leftStringRb.isSelected()) {
            String charset = leftStringCharsetComboBox.getValue();
            resultStr = new String(outputBytes, charset);
        } else {
            throw new OutputTypeException();
        }
        return resultStr;
    }

    private String buildRightStr(byte[] outputBytes) throws OutputTypeException {
        String resultStr;
        if (rightHexRb.isSelected()) {
            byte[] base64Bytes = Base64.encodeBase64(outputBytes);
            resultStr = Hex.encodeHexString(base64Bytes);
        } else if (rightBase64Rb.isSelected()) {
            resultStr = Base64.encodeBase64String(outputBytes);
        } else {
            throw new OutputTypeException();
        }
        return resultStr;
    }
}
